package genetic.operators;

import genetic.interfaces.Crossover;
import genetic.interfaces.Individual;

import java.util.Arrays;

public class CrossoverNPoints<I extends Individual<Integer,Double>> implements Crossover<I> {

	
	private int npoints;
	private int[] points;
	
	public CrossoverNPoints(int _npoints){
		npoints = _npoints;
		points = new int[_npoints + 1];
	}
	
	@Override
	public I crossover(I father, I mother) {
		
		Integer[] fgenes = father.getGenes();
		Integer[] mgenes = mother.getGenes();

//		System.out.println("CrossOver NPoints");
		
		if(npoints >= fgenes.length){
			npoints = fgenes.length - 1;
		}
		
		//Gera numeros aleatórios
		int i = 0;
		points[points.length - 1] = fgenes.length;
		while (i < npoints) {
			Integer random = (int)(Math.random()*fgenes.length); 
			if (!Arrays.asList(points).contains(random)){
				points[i] = random;
				i++;
				Arrays.sort(points);
			}
		}
		//Crosover
		Integer[] primogenito = new Integer[fgenes.length];
		Integer[] cacula = new Integer[fgenes.length];
		Integer[] a = Arrays.copyOfRange(fgenes, 0, points[0]);
		Integer[] b = Arrays.copyOfRange(mgenes, 0, points[0]);
		System.arraycopy(a, 0, primogenito, 0 , points[0]);
		System.arraycopy(b, 0, cacula, 0 , points[0]);
		for (i = 1 ; i < points.length; i++) {
			if (i%2 == 0) {
				b = Arrays.copyOfRange(fgenes, points[i-1], points[i]);
				System.arraycopy(b, 0, primogenito, points[i-1] , points[i] - points[i-1]);
				a = Arrays.copyOfRange(mgenes, points[i-1], points[i]);
				System.arraycopy(a, 0, cacula, points[i-1] , points[i] - points[i-1]);
			}else{
				b = Arrays.copyOfRange(fgenes, points[i-1], points[i]);
				System.arraycopy(b, 0, cacula, points[i-1] , points[i] - points[i-1]);
				a = Arrays.copyOfRange(mgenes, points[i-1], points[i]);
				System.arraycopy(a, 0, primogenito, points[i-1] , points[i] - points[i-1]);
			}
		}
		
		//Cria filho
		@SuppressWarnings("unchecked")
		I filho = (I) father.copy();
		for (i = 0; i < primogenito.length; i++) {
			filho.setGene(i, primogenito[i]);
		}
		@SuppressWarnings("unchecked")
		I filho2 = (I) father.copy();
		for (i = 0; i < cacula.length; i++) {
			filho2.setGene(i, cacula[i]);
		}
		
		if (filho2.eval().compareTo(filho.eval()) > 0){
			return filho2;
		}
		
		return filho;
	}

}
